﻿/* =====================================================================================================
 * C# Lua 5.1 Compiler
 * Copyright (c) agehack a.k.a. GUnitSoldier 2012
 * Source code released under Sun Public License (http://www.opensource.org/licenses/SPL)
===================================================================================================== */

using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace IvmpEditor
{
    class LuaCompiler : ICompiler
    {
        public string InfoText { get { return "Lua (c) lua.org"; } }
        public LuaCompiler(string app_dir) { tempDirectory = new DirectoryInfo(app_dir); }
        public bool IsCompileDone { get { return compiledOk; } }
        public bool IsCompileAborted { get { return !IsCompileDone && abort; } }
        bool abort = false;
        DirectoryInfo tempDirectory = null;
        bool compiledOk = false;
        string scriptName = null;
        DirectoryInfo scriptDirectory = null;
        LockFile lockFile = new LockFile("lua_compiler.lock");
        string logStr = null;

        // Events:
        public event _Compiler.SendString SetStatus;
        public event _Compiler.SendString FatalError;
        public event _Compiler.Signal CompilationStarted;
        public event _Compiler.Signal CompilationFinished;

        // General methods:
        public void Compile(string sourceFileName, Encoding src_encd)
        {
            // Compiler works only with .lua files.
            if (!sourceFileName.ToLower().EndsWith(".lua"))
                throw new NotSupportedException("This is not Lua script source file (.lua)?");

            compiledOk = abort = false;
            logStr = string.Empty;
            string exeFileName = tempDirectory + @"\luac52.exe";
            CompilationStarted(this);
            try
            {
                // Lock our lock file to prevent running several compiler instances
                _Compiler.HoldCompilerLock(lockFile, ref abort);

                // Get scriptname & etc info. Copy script file to compiler directory:
                scriptDirectory = new FileInfo(sourceFileName).Directory;
                int i = sourceFileName.LastIndexOf("\\") + 1;
                scriptName = sourceFileName.Substring(i, sourceFileName.Length - i);
                scriptName = scriptName.Substring(0, scriptName.ToLower().LastIndexOf(".lua"));
                string outFileName = scriptDirectory.FullName + @"\" + scriptName + ".out";

                // Compiler process               
                var luac = new Process();
                luac.StartInfo.FileName = exeFileName;
                luac.StartInfo.Arguments = String.Format("-o \"{1}\" \"{0}\"", sourceFileName, outFileName);
                luac.StartInfo.UseShellExecute = false;
                luac.StartInfo.CreateNoWindow = true;
                luac.StartInfo.RedirectStandardOutput =
                    luac.StartInfo.RedirectStandardError = true;
                luac.EnableRaisingEvents = true;
                luac.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                luac.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);

                // Start compiler process, start listening for output and wait for exit
                this.SetStatus(this, "Compiling script " + scriptName + ".lua...");
                logStr = string.Empty;
                luac.Start();
                luac.PriorityClass = ProcessPriorityClass.Idle;
                luac.BeginOutputReadLine();
                luac.BeginErrorReadLine();
                if (_Compiler.WaitForProcessExit(ref abort, luac))
                    compiledOk = (luac.ExitCode == 0);
                else
                    throw new ThreadStateException();
            }
            catch (Exception ex)
            {
                // Fatal error (something went wrong ...)
                compiledOk = false; 
                this.FatalError(this, ex.Message);
            }

            // Unlock, cleanup, finish
            lockFile.Release();
            CompilationFinished(this);
        }
        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            // Triggered when LUAC outputs text/error to console
            var logLine = e.Data + Environment.NewLine;
            logStr += logLine;
            SetStatus(this, logLine);
        }
        public void RequestAbort() { abort = true; }
    }
}
